﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Text;
using SkinEngines.SkinForm;
using System.Collections;
using SkinEngines.SkinCore;
using SkinEngines.Win32;
using System.Windows.Forms;
using SkinEngines.DefaultSkin;

namespace SkinEngines
{
    /// <summary>
    /// SkinEngine -- SkinEngine is inhirt Component,it used to get handler to skin
    /// </summary>
    public partial class SkinEngine : Component
    {
        #region Constructor
        /// <summary>
        /// Constructor
        /// </summary>
        public SkinEngine()
        {
            InitializeComponent();
            this.InternalConstructor();
        }

        /// <summary>
        /// Constructor with container
        /// </summary>
        /// <param name="container"></param>
        public SkinEngine(IContainer container)
        {
            container.Add(this);
            InitializeComponent();
            this.InternalConstructor();
        }

        /// <summary>
        /// Constructor with Component
        /// </summary>
        /// <param name="owner"></param>
        public SkinEngine(Component owner)
        {
            InitializeComponent();
            this.owner = owner;
            this.InternalConstructor();
        }
        #endregion

        #region Field
        /// <summary>
        /// CBTHook -- Hook WH_CBT
        /// </summary>
        private static CBTHook _cbtHook;

        /// <summary>
        /// Skinned handled
        /// </summary>
        internal static ArrayList SkinHandleList = new ArrayList();

        /// <summary>
        /// Save Skinning Form List
        /// </summary>
        private Hashtable _formList = new Hashtable();

        /// <summary>
        /// Save Skin Dialog List
        /// </summary>
        private Hashtable _dialogList = new Hashtable();

        /// <summary>
        /// current skin
        /// </summary>
        private SkinBase _currentSkin;

        /// <summary>
        /// Hook
        /// </summary>
        private static IntPtr Hook;

        /// <summary>
        /// Skin Engine -- to Skin all form
        /// </summary>
        internal static SkinEngine Engine;

        /// <summary>
        /// owner
        /// </summary>
        private Component owner;

        /// <summary>
        /// Dialogs is skin or not skin
        /// </summary>
        private bool _skinDialogs = true;
        #endregion

        #region Property
        /// <summary>
        /// Gets the current skin.
        /// </summary>
        /// <value>The current skin.</value>
        public SkinBase CurrentSkin
        {
            get
            {
                return _currentSkin ?? LoadDefaultSkin();
            }
            set
            {
                this._currentSkin = value;
                LoadSkin(_currentSkin);
            }
        }

        /// <summary>
        /// SkinDialogs -- Dialogs is skin or not skin
        /// </summary>
        public bool SkinDialogs
        {
            get
            {
                return this._skinDialogs;
            }
            set
            {
                this._skinDialogs = value;
            }
        }
        #endregion

        #region InternalConstructor
        /// <summary>
        /// the Internal Constructor to Create Hook and Get List of all control to skin.
        /// </summary>
        private void InternalConstructor()
        {
            // IsDesignMode == false
            if (!IsDesignMode)
            {
                // Check Engine
                if (Engine == null)
                {
                    // Set Engine
                    Engine = this;

                    // Hook Process
                    if (Hook == IntPtr.Zero)
                    {
                        _cbtHook = new CBTHook(SkinEngine.FnHookProc);
                        Hook = NativeMethod.SetWindowsHookEx(5, _cbtHook, 0, AppDomain.GetCurrentThreadId());
                        Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
                    }
                }
                else if (this.owner is Form)
                {
                    // owner is form
                    Form owner = (Form)this.owner;
                    if (owner.Created)
                    {
                        // add to handle
                        SkinHandleList.Add(owner.Handle);

                    }
                    else
                    {
                        owner.HandleCreated += new EventHandler(owner_HandleCreated);
                    }
                }
            }
        }
        #endregion

        #region owner_HandleCreated
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void owner_HandleCreated(object sender, EventArgs e)
        {
            if (sender is Form)
            {
                Form form = (Form)sender;
                SkinHandleList.Add(form.Handle);
                this.DoAddWnd(form.Handle, true);
            }
        }
        #endregion

        #region Add to Wndlist or Dialoglist
        /// <summary>
        /// Add to Wndlist
        /// </summary>
        /// <param name="handle"></param>
        /// <param name="isManual"></param>
        private void DoAddWnd(IntPtr handle, bool isManual)
        {
            // Get Control
            Control control = Control.FromHandle(handle);
            // Add Control and Skin Control
            if ((control is Form) && !this._formList.ContainsKey(handle))
            {
                this._formList.Add(handle, null);
                SkinningForm skinningForm = new SkinningForm(control as Form, this);
                this._formList[handle] = skinningForm;
            }
        }

        /// <summary>
        /// Add to Wndlist
        /// </summary>
        /// <param name="handle"></param>
        /// <param name="isManual"></param>
        private void DoAddDlg(IntPtr handle, bool isManual)
        {
            // Add Control and Skin Control
            if (!this._dialogList.ContainsKey(handle))
            {
                this._dialogList.Add(handle, null);
                // TODO:Add Skinning Dialog
                SkinningDialog skinningDialog = new SkinningDialog(handle, this);
                this._dialogList[handle] = skinningDialog;
            }
        }
        #endregion

        #region Application_ApplicationExit
        /// <summary>
        /// Application_ApplicationExit
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Application_ApplicationExit(object sender, EventArgs e)
        {
            Engine.Dispose(false);
        }
        #endregion

        #region FnHookProc
        /// <summary>
        /// FnHookProc
        /// </summary>
        /// <param name="nCode"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        private static unsafe IntPtr FnHookProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (Engine != null)
            {
                switch (nCode)
                {
                    case 3:
                        // ToDo: add code to skin
                        break;
                    case 5:
                        // Get Skin Control
                        Control control = Control.FromHandle(wParam);
                        if (control == null)
                        {
                            if (Engine.SkinDialogs)
                            {
                                StringBuilder builder = new StringBuilder(260);
                                NativeMethod.GetClassName(wParam, builder, 260);
                                if (builder.Length == 6 && builder.ToString() == "#32770")
                                {
                                    // Add to SkinHandleList
                                    SkinHandleList.Add(wParam);
                                    Engine.DoAddDlg(wParam, true);
                                }
                            }
                            break;
                        }
                        if (!SkinHandleList.Contains(wParam) && (control is Form))
                        {
                            // Add to SkinHandleList
                            SkinHandleList.Add(wParam);
                            Engine.DoAddWnd(wParam, true);
                        }
                        break;
                    default:
                        break;
                }
            }
            return NativeMethod.CallNextHookEx(Hook, nCode, wParam, lParam);
        }
        #endregion

        #region Method
        /// <summary>
        /// Loads the default skin.
        /// </summary>
        /// <returns></returns>
        private SkinBase LoadDefaultSkin()
        {
            // skin implementation
            _currentSkin = new Skin();

            // load skin
            LoadSkin(_currentSkin);
            return _currentSkin;
        }

        /// <summary>
        /// Loads a skin implementation
        /// </summary>
        /// <param name="skin">The skin.</param>
        public void LoadSkin(SkinBase skin)
        {
            if (skin != null)
            {
                _currentSkin = skin;
                _currentSkin.OnLoad();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether we are currently in design mode.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this we are in design mode; otherwise, <c>false</c>.
        /// </value>
        internal static bool IsDesignMode { get; set; }
        #endregion
    }
}
